/**
 * MIT License
 * 
 * Copyright (c) 2024 - present @ ebraid
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#ifndef __EB_I2C_H__
#define __EB_I2C_H__

#include <kernel/device.h>
#include <kernel/driver.h>
#include <kernel/syslog.h>
#include <kernel/fdt.h>
#include <drivers/platform.h>
#include <drivers/gpio.h>


#define I2C_M_READ    0x01
#define I2C_M_WRITE   0x00


/**
 * @brief i2c bus, it define in the i2c.c file
 */
extern struct bus_type i2c_bus;
struct i2c_driver;
struct i2c_client;


/**
 * @brief i2c transfer struct, it contain the tx and rx buffer and the length
 * @tx_buf: transmit buffer
 * @rx_buf: receive buffer
 * @len: transfer length
 * @flag: transfer flag
 * note: the flag is used to indicate the transfer direction, 0: write, 1: read
 */
struct i2c_transfer {
    void *tx_buf;
    void *rx_buf;
    size_t len;
    uint8_t flag;
};


/**
 * @brief i2c gpio pin struct
 * @sda: sda pin
 * @scl: scl pin
 * note: the pin number is the gpio number
 */
struct i2c_gpio {
    int16_t sda;
    int16_t scl;
};


/**
 * @brief i2c adapter struct, it also define i2c master struct
 * @io_base: i2c io base address
 * @pin: i2c gpio pin
 * @clock_freq_hz: i2c clock frequency
 * @transfer: i2c transfer function
 * @start: i2c start function
 * @stop: i2c stop function
 */
struct i2c_adapter {
    size_t io_base;
    struct i2c_gpio pin;
    size_t clock_freq_hz;
    int (*transfer)(struct i2c_client *client, struct i2c_transfer *xfer, int num_xfer);
};


/**
 * @brief i2c client struct
 * @device: device struct
 * @adapter: i2c adapter
 * @speed: i2c speed
 * @addr: i2c device address
 * @platform_data: platform data
 * note: the i2c device address is 7 bit
 */
struct i2c_client {
    struct device     device;
    struct i2c_adapter *adapter;
    uint32_t          speed;
    uint8_t           addr;
    void              *private_data;
};


/**
 * @brief i2c driver struct
 * @driver: driver struct
 * @probe: probe function
 * @remove: remove function
 * @of_match_table: id table
 */
struct i2c_driver {
    struct driver   driver;
    int (*probe)(struct i2c_client *dev);
    void (*remove)(struct i2c_client *dev);
};


#define  i2c_device    i2c_client  


#ifdef __cplusplus
extern "C" {
#endif


#define  I2C_SYSLOG_INFO(...)   SYSLOG_INFO("I2C: "__VA_ARGS__)
#define  I2C_SYSLOG_ERROR(...)  SYSLOG_ERROR("I2C: "__VA_ARGS__)
#define  I2C_SYSLOG_WARN(...)   SYSLOG_WARN("I2C: "__VA_ARGS__)
#define  I2C_DEBUG_TRACE(...)   DEBUG_TRACE("I2C: "__VA_ARGS__)


/**
 * @brief to get i2c_device struct from device
 * @param dev device struct
 * @return i2c_device struct
 */
static inline  struct i2c_device* to_i2c_device(struct device *dev)
{
    return container_of(dev, struct i2c_device, device);
}


/**
 * @brief to get i2c_driver struct from driver
 * @param drv driver struct
 * @return i2c_driver struct
 */
static inline  struct i2c_driver* to_i2c_driver(struct driver *drv)
{
    return container_of(drv, struct i2c_driver, driver);
}


/**
 * @brief i2c device register function
 * @param dev i2c device struct
 * @return 0 success, -1 failed
 */
static inline int i2c_device_register(struct i2c_device *dev)
{
    dev->device.bus = &i2c_bus;
    return device_register(&dev->device);
}


/**
 * @brief i2c device unregister function
 * @param dev i2c device struct
 * @return none
 */
static inline void i2c_device_unregister(struct i2c_device *dev)
{
    device_unregister(&dev->device);
}


/**
 * @brief i2c driver register function
 * @param drv i2c driver struct
 * @return 0 success, -1 failed
 */
static inline int i2c_driver_register(struct i2c_driver *drv)
{
    drv->driver.bus = &i2c_bus;
    return driver_register(&drv->driver);
}


/**
 * @brief i2c driver unregister function
 * @param drv i2c driver struct
 * @return none
 */
static inline void i2c_driver_unregister(struct i2c_driver *drv)
{
    driver_unregister(&drv->driver);
}


/**
 * @brief to set i2c device driver data
 * @param dev i2c device struct
 * @param data driver data
 * @return none
 */
static inline void i2c_set_drvdata(struct i2c_device *dev, void *data)
{
    device_set_drvdata(&dev->device, data);
}


/**
 * @brief to get i2c device driver data
 * @param dev i2c device struct
 * @return driver data
 */
static inline void* i2c_get_drvdata(struct i2c_device *dev)
{
    return device_get_drvdata(&dev->device);
}


/**
 * @brief to set i2c device platform data
 * @param dev i2c device struct
 * @param data platform data
 * @return none
 */
static inline void i2c_set_platform_data(struct i2c_device *dev, const void *data)
{
    device_set_platform_data(&dev->device, data);
}


/**
 * @brief to get i2c device platform data
 * @param dev i2c device struct
 * @return platform data
 */
static inline const void* i2c_get_platform_data(struct i2c_device *dev)
{
    return device_get_platform_data(&dev->device);
}


/**
 * @brief to set i2c device private data
 * @param dev i2c device struct
 * @param data private data
 * @return none
 */
static inline void i2c_set_private_data(struct i2c_device *dev, void *data)
{
    dev->private_data = data;
}


/**
 * @brief to get i2c device private data
 * @param dev i2c device struct
 * @return private data
 */
static inline void* i2c_get_private_data(struct i2c_device *dev)
{
    return dev->private_data;
}


/**
 * @brief get i2c device gpio pin from device tree
 * @param node: device tree node
 * @param pin: i2c gpio pin
 * @return: 0: success, -1: failed
 */
int i2c_adapter_get_gpio_from_dt(fdt_node_t *node, struct i2c_gpio *pin);


/**
 * @brief get i2c device config from device tree
 * @param node: device tree node
 * @param i2c_dev: i2c device
 * @return: 0: success, -1: failed
 */
int i2c_device_get_config_from_dt(fdt_node_t *node, struct i2c_device *dev);


#ifdef __cplusplus
}
#endif


#endif // __EB_I2C_H__